use super::{gpr, IWord, Riscv64, Word};

use crate::isa::{Cpu, Isa};

pub struct Riscv64Cpu {
    pub(super) gpr: [Word; Riscv64::GPR_NUMBER],
    pub(super) pc: Word,
}

impl Riscv64Cpu {
    pub fn new() -> Self {
        assert!(
            std::mem::size_of::<usize>() >= 8,
            "can't simulate 64bit cpu in {} bit machine",
            std::mem::size_of::<usize>() * 8
        );
        Self {
            gpr: [0; Riscv64::GPR_NUMBER],
            pc: Riscv64::PROG_START,
        }
    }
}

impl Cpu<Word> for Riscv64Cpu {
    /// 调用者需要确保索引小于 32
    fn set_reg(&mut self, idx: usize, value: Word) {
        if idx != gpr::zero {
            self.gpr[idx] = value;
        }
    }

    fn set_regi(&mut self, idx: usize, value: IWord) {
        if idx != gpr::zero {
            self.gpr[idx] = value as Word;
        }
    }

    fn set_pc(&mut self, value: Word) {
        self.pc = value
    }

    /// 调用者需要确保索引小于 32, 不需要assert的原因是，rust 的数组直接索引取值里面已经检查了。。。
    fn reg(&self, idx: usize) -> Word {
        // 因为一直不写 gpr[0], 所以应该一直是 0
        debug_assert!(self.gpr[gpr::zero] == 0);
        self.gpr[idx]
    }

    fn regi(&self, idx: usize) -> IWord {
        // 因为一直不写 gpr[0], 所以应该一直是 0
        debug_assert!(self.gpr[gpr::zero] == 0);
        self.gpr[idx] as IWord
    }

    fn pc(&self) -> Word {
        self.pc
    }
}
